/*-------------------------------------------------------------------------
 *
 * partitionfuncs.c
 *	  访问与分区相关的元数据的函数
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/partitionfuncs.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/partition.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"

/*
 * 检查给定关系是否可以成为分区树的一部分。返回
 * false 如果该关系无法处理，在这种情况下由
 * 调用者决定该做什么，可以通过引发错误或其他方式处理。
 */
static bool fc_check_rel_can_be_partition(Oid fc_relid)
{
	char		fc_relkind;
	bool		fc_relispartition;

	/* 检查关系是否存在 */
	if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(fc_relid)))
		return false;

	fc_relkind = get_rel_relkind(fc_relid);
	fc_relispartition = get_rel_relispartition(fc_relid);

	/* 仅允许出现在分区树中的关系类型。 */
	if (!fc_relispartition && !RELKIND_HAS_PARTITIONS(fc_relkind))
		return false;

	return true;
}

/*
 * pg_partition_tree
 *
 * 生成一个视图，包含每个分区树成员的一行，从调用者提供的最顶层父节点开始。 
 * 这提供了有关每个分区的信息，包括其直接分区父节点，如果它是叶子分区，以及
 * 它在层级中的级别。
 */
Datum pg_partition_tree(PG_FUNCTION_ARGS)
{
#define PG_PARTITION_TREE_COLS	4
	Oid			fc_rootrelid = PG_GETARG_OID(0);
	FuncCallContext *fc_funcctx;
	List	   *fc_partitions;

	/* 仅在函数的第一次调用时完成的工作 */
	if (SRF_IS_FIRSTCALL())
	{
		MemoryContext fc_oldcxt;
		TupleDesc	fc_tupdesc;

		/* 为跨调用持久性创建一个函数上下文 */
		fc_funcctx = SRF_FIRSTCALL_INIT();

		if (!fc_check_rel_can_be_partition(fc_rootrelid))
			SRF_RETURN_DONE(fc_funcctx);

		/* 切换到适合多个函数调用的内存上下文 */
		fc_oldcxt = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/*
		 * 查找所有继承集的成员。我们只需要对子节点进行 AccessShareLock
		 * 以便查找分区信息。
		 */
		fc_partitions = find_all_inheritors(fc_rootrelid, AccessShareLock, NULL);

		fc_tupdesc = CreateTemplateTupleDesc(PG_PARTITION_TREE_COLS);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 1, "relid",
						   REGCLASSOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 2, "parentid",
						   REGCLASSOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 3, "isleaf",
						   BOOLOID, -1, 0);
		TupleDescInitEntry(fc_tupdesc, (AttrNumber) 4, "level",
						   INT4OID, -1, 0);

		fc_funcctx->tuple_desc = BlessTupleDesc(fc_tupdesc);

		/* 我们唯一需要的状态是分区列表 */
		fc_funcctx->user_fctx = (void *) fc_partitions;

		MemoryContextSwitchTo(fc_oldcxt);
	}

	/* 每次调用函数时完成的工作 */
	fc_funcctx = SRF_PERCALL_SETUP();
	fc_partitions = (List *) fc_funcctx->user_fctx;

	if (fc_funcctx->call_cntr < list_length(fc_partitions))
	{
		Datum		fc_result;
		Datum		fc_values[PG_PARTITION_TREE_COLS];
		bool		fc_nulls[PG_PARTITION_TREE_COLS];
		HeapTuple	fc_tuple;
		Oid			fc_parentid = InvalidOid;
		Oid			fc_relid = list_nth_oid(fc_partitions, fc_funcctx->call_cntr);
		char		fc_relkind = get_rel_relkind(fc_relid);
		int			fc_level = 0;
		List	   *fc_ancestors = get_partition_ancestors(fc_relid);
		ListCell   *fc_lc;

		/*
		 * 使用适当的数据形成元组。
		 */
		MemSet(fc_nulls, 0, sizeof(fc_nulls));
		MemSet(fc_values, 0, sizeof(fc_values));

		/* relid */
		fc_values[0] = ObjectIdGetDatum(fc_relid);

		/* parentid */
		if (fc_ancestors != NIL)
			fc_parentid = linitial_oid(fc_ancestors);
		if (OidIsValid(fc_parentid))
			fc_values[1] = ObjectIdGetDatum(fc_parentid);
		else
			fc_nulls[1] = true;

		/* isleaf */
		fc_values[2] = BoolGetDatum(!RELKIND_HAS_PARTITIONS(fc_relkind));

		/* level */
		if (fc_relid != fc_rootrelid)
		{
			foreach(fc_lc, fc_ancestors)
			{
				fc_level++;
				if (lfirst_oid(fc_lc) == fc_rootrelid)
					break;
			}
		}
		fc_values[3] = Int32GetDatum(fc_level);

		fc_tuple = heap_form_tuple(fc_funcctx->tuple_desc, fc_values, fc_nulls);
		fc_result = HeapTupleGetDatum(fc_tuple);
		SRF_RETURN_NEXT(fc_funcctx, fc_result);
	}

	/* 当没有更多元素时完成 */
	SRF_RETURN_DONE(fc_funcctx);
}

/*
 * pg_partition_root
 *
 * 返回给定关系所属的分区树的最顶层父节点，如果它不是（或不能是）
 * 任何分区树的一部分，则返回 NULL。
 */
Datum pg_partition_root(PG_FUNCTION_ARGS)
{
	Oid			fc_relid = PG_GETARG_OID(0);
	Oid			fc_rootrelid;
	List	   *fc_ancestors;

	if (!fc_check_rel_can_be_partition(fc_relid))
		PG_RETURN_NULL();

	/* 获取祖先列表 */
	fc_ancestors = get_partition_ancestors(fc_relid);

	/*
	 * 如果输入关系已经是最顶层父节点，则直接返回
	 * 它自己。
	 */
	if (fc_ancestors == NIL)
		PG_RETURN_OID(fc_relid);

	fc_rootrelid = llast_oid(fc_ancestors);
	list_free(fc_ancestors);

	/*
	 * "rootrelid" 必须包含一个有效的 OID，因为输入关系是
	 * 上述检查的有效分区树成员。
	 */
	Assert(OidIsValid(fc_rootrelid));
	PG_RETURN_OID(fc_rootrelid);
}

/*
 * pg_partition_ancestors
 *
 * 生成一个视图，包含给定分区的每个祖先的一行，
 * 包括输入关系本身。
 */
Datum pg_partition_ancestors(PG_FUNCTION_ARGS)
{
	Oid			fc_relid = PG_GETARG_OID(0);
	FuncCallContext *fc_funcctx;
	List	   *fc_ancestors;

	if (SRF_IS_FIRSTCALL())
	{
		MemoryContext fc_oldcxt;

		fc_funcctx = SRF_FIRSTCALL_INIT();

		if (!fc_check_rel_can_be_partition(fc_relid))
			SRF_RETURN_DONE(fc_funcctx);

		fc_oldcxt = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		fc_ancestors = get_partition_ancestors(fc_relid);
		fc_ancestors = lcons_oid(fc_relid, fc_ancestors);

		/* 我们唯一需要的状态是祖先列表 */
		fc_funcctx->user_fctx = (void *) fc_ancestors;

		MemoryContextSwitchTo(fc_oldcxt);
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_ancestors = (List *) fc_funcctx->user_fctx;

	if (fc_funcctx->call_cntr < list_length(fc_ancestors))
	{
		Oid			fc_relid = list_nth_oid(fc_ancestors, fc_funcctx->call_cntr);

		SRF_RETURN_NEXT(fc_funcctx, ObjectIdGetDatum(fc_relid));
	}

	SRF_RETURN_DONE(fc_funcctx);
}
